package redis

import (
	"common/helper"
	"context"
	"database/sql"
	"fmt"
	"github.com/go-redis/redis/v8"
	"github.com/jmoiron/sqlx"
	"github.com/shopspring/decimal"
	"strconv"
	"strings"
	"time"
)

const defaultExpires = time.Duration(1) * time.Hour

var UpdateRoleSetKey = "UpdateRoleSet"
var ImmediateRoleSetKey = "ImmediatelyRoleSet"
var InsertLogKey = "insertLogKey"
var UpdateSqlField = "UpdateSqlField"
var UpdateTransaction = "UpdateTransaction"
var gTx context.Context
var gRedis *redis.Client
var gMasterDBCon *sqlx.DB
var gSlaveDBCon *sqlx.DB

func GetMasterDBInstance() *sqlx.DB {
	return gMasterDBCon
}

func GetSlaveDBInstance() *sqlx.DB {
	return gSlaveDBCon
}

func GetRedisInstance() *redis.Client {
	return gRedis
}

type FuncLoadUserToRedis func(uid string)

var g_funcLoadUserToRedis FuncLoadUserToRedis

func Init(tx context.Context, redis *redis.Client, MastersqlCon *sqlx.DB, SlavesqlCon *sqlx.DB, funcLoadUserToRedis FuncLoadUserToRedis) {
	gTx = tx
	gRedis = redis
	gMasterDBCon = MastersqlCon
	gSlaveDBCon = SlavesqlCon
	g_funcLoadUserToRedis = funcLoadUserToRedis
}

// 增加日志到redis 由 rpc_server定时入库
func AddLogToRedis(strSQl string) {

	//后期优化:可以以表表做Key 定时插入时可以N条一起insert
	gRedis.LPush(gTx, InsertLogKey, strSQl)
}

// update insert 接口  由 rpc_server定时入库 优先级比 addLog
func UpdateSqlFieldToRedis(strSQl string) {
	gRedis.LPush(gTx, UpdateSqlField, strSQl)
}

// update insert 接口  由 rpc_server定时入库 优先级比 addLog
func UpdateTransactionToRedis(trans []byte) {
	gRedis.LPush(gTx, UpdateTransaction, trans)
}

func IsUserExis(uid string) bool {
	redisKey := fmt.Sprintf("UserId:%s", uid)
	if gRedis.Exists(gTx, redisKey).Val() > 0 {
		return true
	} else {
		return false
	}
}

/*
统一在redis中设置用户的属性
*/
func checkParam(uid string, field string) bool {

	ssData := strings.Split(field, ":")
	if len(ssData) < 2 {
		return false
	}

	/*
		if uid < 10000000 || uid >= 100000000 {
			return false
		}
	*/

	return true
}

/*
设置用户更新的标志
*/
func setUpdateLabel(uid string, tableName string, tableField string) {
	redisKey := fmt.Sprintf("UserId:%s", uid)
	updateField, _ := gRedis.HGet(gTx, redisKey, tableName).Result()
	var bNeedUpdate bool = false
	if updateField == "" {
		updateField += tableField
		bNeedUpdate = true
	} else {
		var bContain bool = false
		tableFields := strings.Split(updateField, ":")
		for arrIndex := 0; arrIndex < len(tableFields); arrIndex++ {
			if tableFields[arrIndex] == tableField {
				bContain = true
				break
			}
		}

		if !bContain {
			updateField += (":" + tableField)
			bNeedUpdate = true
		}
	}

	if bNeedUpdate {
		gRedis.HSet(gTx, redisKey, tableName, updateField)
		gRedis.Expire(gTx, redisKey, defaultExpires)
		//在"UpdateRoleSet"集合中设置需要更新的用户ID DB定时更新时不需要检索整个redis hash
		gRedis.SAdd(gTx, UpdateRoleSetKey, uid)
		gRedis.Expire(gTx, UpdateRoleSetKey, defaultExpires)
	}
}

func AddUserFieldValueByInt64(uid string, field string, incr int64) bool {

	if !checkParam(uid, field) {
		return false
	}

	dataFieldLock := fmt.Sprintf("%s", uid)
	if !Lock(gRedis, dataFieldLock) {
		helper.GetloggerInstance().Emergencyf("AddUserFieldValueByInt64 lock fail:%s(%s:%s:%d)", dataFieldLock, uid, field, incr)
		return false
	}

	defer Unlock(gRedis, dataFieldLock)

	g_funcLoadUserToRedis(uid)
	ssData := strings.Split(field, ":")
	tableName := ssData[0]
	tableField := ssData[1]

	redisKey := fmt.Sprintf("UserId:%s", uid)
	valExsit, err := gRedis.HExists(gTx, redisKey, field).Result()
	if err != nil {
		helper.InfoLog("AddUserFieldValueByInt64 HExists err, %s", err.Error())
		return false
	}

	var FieldValueFromSQL int64 = 0
	if !valExsit && strings.Contains(tableName, "tbl_") {
		//查询数据库对应表中的字段
		exceSql := fmt.Sprintf("Select %s from %s where uid=%s", tableField, tableName, uid)
		err = gMasterDBCon.Get(&FieldValueFromSQL, exceSql)
		if err != nil {
			helper.SqlSelectErrLog("AddUserFieldValueByInt64 %s,\n %s", err.Error(), exceSql)
		}
		if err != nil && err.Error() != "sql: no rows in result set" {
			return false
		}
	}

	lastUpdateValue := FieldValueFromSQL + incr
	_, err = gRedis.HIncrBy(gTx, redisKey, field, lastUpdateValue).Result()
	if err != nil {
		helper.GetloggerInstance().Emergencyf("HIncrByFloat: %s	%s	%d reason:%s", redisKey, field, lastUpdateValue, err.Error())
	}

	setUpdateLabel(uid, tableName, tableField)
	return true
}

func AddUserFieldValueByFloat64(uid string, field string, incr float64) bool {

	if !checkParam(uid, field) {
		return false
	}

	dataFieldLock := fmt.Sprintf("%s", uid)
	if !Lock(gRedis, dataFieldLock) {
		helper.GetloggerInstance().Emergencyf("AddUserFieldValueByFloat64 lock fail:%s(%s:%s:%0.2f)", dataFieldLock, uid, field, incr)
		return false
	}

	defer Unlock(gRedis, dataFieldLock)

	g_funcLoadUserToRedis(uid)
	ssData := strings.Split(field, ":")
	tableName := ssData[0]
	tableField := ssData[1]

	redisKey := fmt.Sprintf("UserId:%s", uid)
	valExsit, err := gRedis.HExists(gTx, redisKey, field).Result()
	if err != nil {
		helper.InfoLog("AddUserFieldValueByFloat64 HExists err, %s", err.Error())
		return false
	}

	var FieldValueFromSQL float64
	FieldValueFromSQL = 0.00

	if !valExsit {
		//查询数据库对应表中的字段
		exceSql := fmt.Sprintf("Select %s from %s where uid=%s", tableField, tableName, uid)
		err = gMasterDBCon.Get(&FieldValueFromSQL, exceSql)
		if err != nil {
			helper.SqlSelectErrLog("AddUserFieldValueByFloat64 sql err, %s \n %s", err.Error(), exceSql)
		}

		if err != nil && err.Error() != "sql: no rows in result set" {
			return false
		}
	}

	lastUpdateValue := FieldValueFromSQL + incr
	_, err = gRedis.HIncrByFloat(gTx, redisKey, field, lastUpdateValue).Result()
	if err != nil {
		helper.GetloggerInstance().Emergencyf("HIncrByFloat: %s	%s	%0.2f reason:%s", redisKey, field, lastUpdateValue, err.Error())
	}

	setUpdateLabel(uid, tableName, tableField)
	return true
}

func AddUserFieldValueByDecimal(uid string, field string, incr decimal.Decimal) {

	//if !checkParam(uid, field) {
	//	return false
	//}

	g_funcLoadUserToRedis(uid)
	fAddValue, _ := incr.Float64()
	//if bSuccess {
	AddUserFieldValueByFloat64(uid, field, fAddValue)
	//}
	//return bSuccess
}

func MinusUserFieldValueByDecimal(uid string, field string, incr decimal.Decimal) {

	//if !checkParam(uid, field) {
	//	return false
	//}

	g_funcLoadUserToRedis(uid)
	fAddValue, _ := incr.Float64()
	//if bSuccess {
	AddUserFieldValueByFloat64(uid, field, -fAddValue)
	//}
	//return bSuccess
}

func SetUserFieldValue(uid string, field string, strValue string) bool {

	if !checkParam(uid, field) {
		return false
	}

	dataFieldLock := fmt.Sprintf("%s", uid)

	if !Lock(gRedis, dataFieldLock) {
		helper.GetloggerInstance().Emergencyf("SetUserFieldValue lock fail:%s(%s:%s:%s)", dataFieldLock, uid, field, strValue)
		return false
	}

	defer Unlock(gRedis, dataFieldLock)

	g_funcLoadUserToRedis(uid)
	ssData := strings.Split(field, ":")
	tableName := ssData[0]
	tableField := ssData[1]

	redisKey := fmt.Sprintf("UserId:%s", uid)
	_, err := gRedis.HSet(gTx, redisKey, field, strValue).Result()
	if err != nil {
		helper.GetloggerInstance().Emergencyf("HSet: %s	%s	%s reason:%s", redisKey, field, strValue, err.Error())
	}
	setUpdateLabel(uid, tableName, tableField)
	return true
}

func SetUserFieldNotLab(uid string, field string, strValue string) bool {
	if !checkParam(uid, field) {
		return false
	}

	redisKey := fmt.Sprintf("UserId:%s", uid)
	_, err := gRedis.HSet(gTx, redisKey, field, strValue).Result()
	if err != nil {
		helper.GetloggerInstance().Emergencyf("HSet: %s	%s	%s reason:%s", redisKey, field, strValue, err.Error())
	}

	return true
}

func SetUserFieldValueByInt64(uid string, field string, nVal int64) bool {
	redisValue := fmt.Sprintf("%d", nVal)
	gRedis.Expire(gTx, redisValue, defaultExpires)
	return SetUserFieldValue(uid, field, redisValue)
}

func SetUserFieldValueByInt(uid string, field string, nVal int) bool {
	redisValue := fmt.Sprintf("%d", nVal)
	gRedis.Expire(gTx, redisValue, defaultExpires)
	return SetUserFieldValue(uid, field, redisValue)
}

func SetUserFieldValueByFloat64(uid string, field string, fVal float64) bool {
	redisValue := fmt.Sprintf("%0.2f", fVal)
	gRedis.Expire(gTx, redisValue, defaultExpires)
	return SetUserFieldValue(uid, field, redisValue)
}

func GetUserRedisKey(uid string) string {
	return fmt.Sprintf("UserId:%s", uid)
}

func GetUserFieldString(uid string, field string) string {
	var retValue string
	if !checkParam(uid, field) {
		return ""
	}

	redisKey := fmt.Sprintf("UserId:%s", uid)
	retValue, err := gRedis.HGet(gTx, redisKey, field).Result()

	if err == nil {
		return retValue
	}
	g_funcLoadUserToRedis(uid)
	ssData := strings.Split(field, ":")
	tableName := ssData[0]
	tableField := ssData[1]

	//查询数据库对应表中的字段
	exceSql := fmt.Sprintf("Select %s from %s where uid=%s", tableField, tableName, uid)
	errSql := gMasterDBCon.Get(&retValue, exceSql)
	if errSql == sql.ErrNoRows {
		return retValue
	}
	if errSql != nil {
		helper.SqlSelectErrLog("GetUserFieldString sql err, %s \n %s", errSql.Error(), exceSql)
		return retValue
	}

	_, err = gRedis.HSet(gTx, redisKey, field, retValue).Result()
	if err != nil {
		helper.GetloggerInstance().Emergencyf("HSet: %s	%s	%s reason:%s", redisKey, field, retValue, err.Error())
	}
	return retValue

}

func GetUserFieldUint32(uid string, field string) uint32 {
	strRetValue := GetUserFieldString(uid, field)
	nValue, err := strconv.ParseInt(strRetValue, 10, 64)
	if err != nil {
		return 0
	} else {
		return uint32(nValue)
	}
}

func GetUserFieldInt32(uid string, field string) int32 {
	strRetValue := GetUserFieldString(uid, field)
	nValue, err := strconv.ParseInt(strRetValue, 10, 64)
	if err != nil {
		return 0
	} else {
		return int32(nValue)
	}
}

func GetUserFieldInt(uid string, field string) int {
	strRetValue := GetUserFieldString(uid, field)
	nValue, err := strconv.ParseInt(strRetValue, 10, 64)
	if err != nil {
		return 0
	} else {
		return int(nValue)
	}
}

func GetUserFieldUint8(uid string, field string) uint8 {
	strRetValue := GetUserFieldString(uid, field)
	nValue, err := strconv.ParseInt(strRetValue, 10, 64)
	if err != nil {
		return 0
	} else {
		return uint8(nValue)
	}
}

func GetUserFieldInt64(uid string, field string) int64 {
	strRetValue := GetUserFieldString(uid, field)
	nValue, err := strconv.ParseInt(strRetValue, 10, 64)
	if err != nil {
		return 0
	} else {
		return nValue
	}
}
func GetUserFieldFloat64(uid string, field string) float64 {
	strRetValue := GetUserFieldString(uid, field)
	fValue, err := strconv.ParseFloat(strRetValue, 64)
	if err != nil {
		return 0.0
	} else {
		return fValue
	}
}

func GetUserFieldString64NoSelect(uid string, field string) string {

	var retValue string
	if !checkParam(uid, field) {
		return ""
	}

	redisKey := fmt.Sprintf("UserId:%s", uid)
	retValue, _ = gRedis.HGet(gTx, redisKey, field).Result()
	return retValue
}
func GetUserFieldInt64NoSelect(uid string, field string) int64 {

	var retValue string
	if !checkParam(uid, field) {
		return 0.00
	}

	redisKey := fmt.Sprintf("UserId:%s", uid)
	retValue, err := gRedis.HGet(gTx, redisKey, field).Result()

	fValue, err := strconv.ParseInt(retValue, 10, 64)
	if err != nil {
		return 0
	} else {
		return fValue
	}
}

func GetUserFieldFloat64NoSelect(uid string, field string) float64 {

	var retValue string
	if !checkParam(uid, field) {
		return 0.00
	}

	redisKey := fmt.Sprintf("UserId:%s", uid)
	retValue, err := gRedis.HGet(gTx, redisKey, field).Result()

	fValue, err := strconv.ParseFloat(retValue, 64)
	if err != nil {
		return 0.0
	} else {
		return fValue
	}
}

func AddProxyBusiDataByFloat64(uid string, field string, incr float64) bool {

	dataFieldLock := fmt.Sprintf("%s", uid)
	if !Lock(gRedis, dataFieldLock) {
		helper.GetloggerInstance().Emergencyf("AddProxyBusiDataByFloat64 lock fail:%s(%s:%s:%0.2f)", dataFieldLock, uid, field, incr)
		return false
	}

	defer Unlock(gRedis, dataFieldLock)

	redisKey := fmt.Sprintf("UserId:%s", uid)

	_, err := gRedis.HIncrByFloat(gTx, redisKey, field, incr).Result()
	if err != nil {
		helper.GetloggerInstance().Emergencyf("HSet: %s	%s	%0.2f reason:%s", redisKey, field, incr, err.Error())
	}

	gRedis.SAdd(gTx, UpdateRoleSetKey, uid)
	gRedis.Expire(gTx, UpdateRoleSetKey, defaultExpires)
	gRedis.Expire(gTx, redisKey, defaultExpires)
	return true
}

func ImmediateUpdateRole(uid string) {
	gRedis.SAdd(gTx, ImmediateRoleSetKey, uid)
}
